﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using CMissionLib;
using CMissionLib.Actions;
using CMissionLib.Conditions;
using Action = CMissionLib.Action;

namespace MissionEditor2
{
	/// <summary>
	/// Here go templates that need to be reloaded each time.
	/// </summary>
	public partial class ListTemplates : UserControl
	{
		public ListTemplates()
		{
			InitializeComponent();
			camera.Width = 50;
			camera.Height = 50;
		}

		void Triggers_ListLoaded(object sender, RoutedEventArgs e)
		{
			var list = (ListBox) e.Source;
			var collection = ((TriggersAction) MainWindow.Instance.CurrentLogic).Triggers;
			list.BindCollection(collection);
		}

		void UnitDefGrid_Loaded(object sender, RoutedEventArgs e)
		{
			var dataGrid = ((UnitDefsGrid)e.Source).Grid;
			var currentLogic = MainWindow.Instance.CurrentLogic;
			ObservableCollection<string> units = null;
			if (currentLogic is UnitCreatedCondition)
			{
				units = ((UnitCreatedCondition)currentLogic).Units;
			}
			else if (currentLogic is UnitFinishedCondition)
			{
				units = ((UnitFinishedCondition)currentLogic).Units;
			}
			else if (currentLogic is LockUnitsAction)
			{
				units = ((LockUnitsAction)currentLogic).Units;
			}
			else if (currentLogic is UnlockUnitsAction)
			{
				units = ((UnlockUnitsAction)currentLogic).Units;
			}

			if (units == null) return;

			foreach (var unit in units.ToArray())
			{
				var unitInfo = MainWindow.Instance.Mission.Mod.UnitDefs.FirstOrDefault(u => u.Name == unit);
				if (unitInfo != null)
				{
					dataGrid.SelectedItems.Add(unitInfo);
				}
			}
			SelectionChangedEventHandler handler = (s, se) =>
				{
					foreach (var item in se.AddedItems)
					{
						units.Add(item.ToString());
					}
					foreach (var item in se.RemovedItems)
					{
						units.Remove(item.ToString());
					}
				};
			dataGrid.SelectionChanged += handler;
			// this needs to be done before "Unloaded" because at that point the items will have been all deselected
			MainWindow.Instance.LogicGrid.SelectionChanged += (s, ea) => dataGrid.SelectionChanged -= handler;
		}


		void PlayersList_Loaded(object sender, RoutedEventArgs e)
		{
			var unitList = (ListBox) e.Source;
			var currentLogic = MainWindow.Instance.CurrentLogic;
			if (currentLogic is UnitCreatedCondition)
			{
				unitList.BindCollection(((UnitCreatedCondition) currentLogic).Players);
			}
			else if (currentLogic is UnitFinishedCondition)
			{
				unitList.BindCollection(((UnitFinishedCondition)currentLogic).Players);
			}
		}

		Canvas flagPole = (Canvas) MainWindow.Vectors.FindResource("flagPole");
		Point poleBase = new Point(0, 0);
		Viewbox camera = (Viewbox) MainWindow.Vectors.FindResource("camera");

		void MarkerPointCanvas_Loaded(object sender, RoutedEventArgs e)
		{
			var currentLogic = MainWindow.Instance.CurrentLogic;
			if (currentLogic is MarkerPointAction)
			{
				var action = (MarkerPointAction) currentLogic;
				var markerCanvas = (Canvas) e.Source;
				markerCanvas.Children.Add(flagPole);
				foreach (var unit in MainWindow.Instance.Mission.AllUnits) markerCanvas.PlaceUnit(unit);
				System.Action refreshPosition = delegate
					{
						Canvas.SetLeft(flagPole, action.X - poleBase.X);
						Canvas.SetTop(flagPole, action.Y - flagPole.Height + poleBase.Y);
					};
				refreshPosition();
				markerCanvas.MouseDown += (s, ea) =>
					{
						var mousePos = ea.GetPosition(markerCanvas);
						action.X = mousePos.X;
						action.Y = mousePos.Y;
						refreshPosition();
					};
				markerCanvas.Unloaded += (s, ea) => markerCanvas.Children.Clear();
			}
			else if (currentLogic is SetCameraPointTargetAction)
			{
				var action = (SetCameraPointTargetAction) currentLogic;
				var markerCanvas = (Canvas) e.Source;
				markerCanvas.Children.Add(camera);
				foreach (var unit in MainWindow.Instance.Mission.AllUnits) markerCanvas.PlaceUnit(unit);
				System.Action refreshPosition = delegate
					{
						Canvas.SetLeft(camera, action.X - poleBase.X);
						Canvas.SetTop(camera, action.Y - flagPole.Height + poleBase.Y);
					};
				markerCanvas.MouseDown += (s, ea) =>
				{
					var mousePos = ea.GetPosition(markerCanvas);
					action.X = mousePos.X;
					action.Y = mousePos.Y;
					refreshPosition();
				};
			}
		}

		void UnitEnteredAreaGroupsList_Loaded(object sender, RoutedEventArgs e)
		{
			var condition = (UnitsAreInAreaCondition) MainWindow.Instance.CurrentLogic;
			var listBox = (ListBox) e.Source;
			listBox.BindCollection(condition.Groups);
		}

		void AlliancesList_Loaded(object sender, RoutedEventArgs e)
		{
			var logic = (UnitsAreInAreaCondition) MainWindow.Instance.CurrentLogic;
			var listBox = (ListBox) e.Source;
			listBox.BindCollection(logic.Players);
		}

		// fixme: when the dockpane is set to floating, only circles can be created
		void UnitAreaCanvas_Loaded(object sender, RoutedEventArgs e)
		{
			var canvas = (Canvas) e.Source;
			var tray = (ToolBarTray) canvas.Tag;
			tray.ToolBars.Clear();
			canvas.Children.Clear();

			var menu = new ToolBar();
			tray.ToolBars.Add(menu);

			Func<string, ToggleButton> addButton = name =>
				{
					var buttons = new List<ToggleButton>();
					var button = new ToggleButton {Content = name, IsChecked = false};
					menu.Items.Add(button);
					buttons.Add(button);
					button.Checked += delegate
						{
							foreach (var b in buttons)
							{
								if (b != button)
								{
									b.IsChecked = false;
								}
							}
						};
					return button;
				};

			var circleButton = addButton("Circle");
			var rectangleButton = addButton("Rectangle");
			var deleteButton = addButton("Delete");
			circleButton.IsChecked = true;

			var condition = (UnitsAreInAreaCondition) MainWindow.Instance.CurrentLogic;
			foreach (var unit in MainWindow.Instance.Mission.AllUnits)
			{
				canvas.PlaceUnit(unit);
			}
			var areaInfos = new List<AreaDragInfo>();
			var centerSize = 10.0;

			Func<Cylinder, CircleDragInfo> addCylinder = area =>
				{
					var info = new CircleDragInfo();
					info.Area = area;
					info.Center = new Ellipse {Fill = Brushes.Yellow, Width = centerSize, Height = centerSize, Opacity = 0.5};
					info.Circle = new Ellipse
						{
							Fill = Brushes.Red,
							StrokeThickness = 5,
							Opacity = 0.3,
							Stroke = Brushes.Yellow,
							Width = area.R*2,
							Height = area.R*2
						};
					Canvas.SetLeft(info.Circle, area.X - area.R);
					Canvas.SetTop(info.Circle, area.Y - area.R);
					Canvas.SetLeft(info.Center, area.X - centerSize/2.0);
					Canvas.SetTop(info.Center, area.Y - centerSize/2.0);
					areaInfos.Add(info);
					canvas.Children.Add(info.Circle);
					canvas.Children.Add(info.Center);
					return info;
				};

			Func<RectangularArea, RectangleDragInfo> addRectangle = area =>
				{
					var info = new RectangleDragInfo();
					info.Area = area;
					info.Rectangle = new Rectangle
						{
							Fill = Brushes.Red,
							StrokeThickness = 5,
							Opacity = 0.3,
							Stroke = Brushes.Yellow,
							Width = area.Width,
							Height = area.Height
						};
					info.StartPoint = new Point(area.X, area.Y);
					Canvas.SetLeft(info.Rectangle, area.X);
					Canvas.SetTop(info.Rectangle, area.Y);
					areaInfos.Add(info);
					return info;
				};

			foreach (var area in condition.Areas)
			{
				if (area is Cylinder)
				{
					var cylinder = (Cylinder) area;
					addCylinder(cylinder);
				}
				else if (area is RectangularArea)
				{
					addRectangle((RectangularArea) area);
				}
				else throw new Exception("Unexpected Area");
			}

			AreaDragInfo dragInfo = new NoArea();

			canvas.MouseDown += (s, eventArgs) =>
				{
					if (Keyboard.Modifiers == ModifierKeys.None)
					{
						eventArgs.Handled = true;
						var startPoint = eventArgs.GetPosition(canvas);
						if (dragInfo is NoArea)
						{
							if (circleButton.IsChecked == true)
							{
								// create circle
								canvas.CaptureMouse();
								var area = new Cylinder(startPoint.X, startPoint.Y, 0);
								var info = addCylinder(area);
								dragInfo = info;
								condition.Areas.Add(info.Area);
							}
							else if (rectangleButton.IsChecked == true)
							{
								// create rectangle
								canvas.CaptureMouse();
								var area = new RectangularArea(startPoint.X, startPoint.Y, 0, 0);
								var info = addRectangle(area);
								dragInfo = info;
								condition.Areas.Add(info.Area);
							}
							else if (deleteButton.IsChecked == true && eventArgs.Source is Shape)
							{
								// delete shape
								foreach (var info in areaInfos)
								{
									// remove area

									var circleDragInfo = info as CircleDragInfo;
									if (circleDragInfo != null &&
									    (circleDragInfo.Center == eventArgs.Source || circleDragInfo.Circle == eventArgs.Source))
									{
										areaInfos.Remove(circleDragInfo);
										condition.Areas.Remove(circleDragInfo.Area);
										canvas.Children.Remove(circleDragInfo.Center);
										canvas.Children.Remove(circleDragInfo.Circle);
									}
									var rectangleDragInfo = info as RectangleDragInfo;
									if (rectangleDragInfo != null && (rectangleDragInfo.Rectangle == eventArgs.Source))
									{
										areaInfos.Remove(rectangleDragInfo);
										condition.Areas.Remove(rectangleDragInfo.Area);
										canvas.Children.Remove(rectangleDragInfo.Rectangle);
									}
									if (info is NoArea) throw new Exception("NoArea should not be in the list");
								}
							}
						}
					}
				};

			MainWindow.Instance.PreviewMouseMove += (s, ea) =>
				{
					var mousePosition = ea.GetPosition(canvas);
					if (canvas.IsMouseCaptured)
					{
						if (dragInfo is CircleDragInfo)
						{
							var info = (CircleDragInfo) dragInfo;
							var r = Utils.GetDistance(info.Area.X, info.Area.Y, mousePosition.X, mousePosition.Y);
							info.Area.R = r;
							info.Circle.Width = r*2;
							info.Circle.Height = r*2;
							Canvas.SetLeft(info.Circle, info.Area.X - r);
							Canvas.SetTop(info.Circle, info.Area.Y - r);
						}
						else if (dragInfo is RectangleDragInfo)
						{
							var info = (RectangleDragInfo) dragInfo;
							var dx = mousePosition.X - info.StartPoint.X;
							var dy = mousePosition.Y - info.StartPoint.Y;

							if (dx > 0)
							{
								Canvas.SetLeft(info.Rectangle, info.StartPoint.X);
								info.Rectangle.Width = dx;
								info.Area.X = info.StartPoint.X;
								info.Area.Width = dx;
							}
							else
							{
								Canvas.SetLeft(info.Rectangle, info.StartPoint.X + dx);
								info.Rectangle.Width = -dx;
								info.Area.X = info.StartPoint.X + dx;
								info.Area.Width = -dx;
							}

							if (dy > 0)
							{
								Canvas.SetTop(info.Rectangle, info.StartPoint.Y);
								info.Rectangle.Height = dy;
								info.Area.Y = info.StartPoint.Y;
								info.Area.Height = dy;
							}
							else
							{
								Canvas.SetTop(info.Rectangle, info.StartPoint.Y + dy);
								info.Rectangle.Height = -dy;
								info.Area.Y = info.StartPoint.Y + dy;
								info.Area.Height = -dy;
							}
						}
					}
				};
			canvas.PreviewMouseUp += (s, ea) =>
				{
					if (!(dragInfo is NoArea))
					{
						ea.Handled = true;
						canvas.ReleaseMouseCapture();
						dragInfo = new NoArea();
					}
				};
		}



		void AddCounterButton_Click(object sender, RoutedEventArgs e)
		{
			var dialog = new StringRequest {Title = "Insert counter name."};
			var result = dialog.ShowDialog();
			if (result.HasValue && result.Value) MainWindow.Instance.Mission.Counters.Add(dialog.TextBox.Text);
		}

		void RemoveCounterButton_Click(object sender, RoutedEventArgs e)
		{
			var dialog = new StringRequest {Title = "Insert counter name."};
			var result = dialog.ShowDialog();
			if (result.HasValue && result.Value && !MainWindow.Instance.Mission.Counters.Remove(dialog.TextBox.Text))
			{
				MessageBox.Show("Error: counter " + dialog.TextBox.Text + " does not exist.");
			}
		}

		#region Nested type: AreaDragInfo

		class AreaDragInfo
		{
		}

		#endregion

		#region Nested type: CircleDragInfo

		class CircleDragInfo : AreaDragInfo
		{
			public Cylinder Area;
			public Ellipse Center;
			public Ellipse Circle;
		}

		#endregion

		#region Nested type: NoArea

		class NoArea : AreaDragInfo
		{
		}

		#endregion

		#region Nested type: RectangleDragInfo

		class RectangleDragInfo : AreaDragInfo
		{
			public RectangularArea Area;
			public Rectangle Rectangle;
			public Point StartPoint;
		}

		#endregion
	}
}